using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Net;
using System.IO;
using System.Threading;
using System.Linq;
using System.Web;
using Helpers;
using Helpers.Extensions;
using Helpers;
using Common.Constants;


namespace HttpRequester.RequestMakers.Base
{
    public abstract class RequestManagerBase : RequestInfo
    {
        public List<Cookie> OutputCookies { get; set; }
        public HttpWebResponse Response { get; set; }



        public RequestManagerBase() : base()
        {
            Clear();
        }


        
        public override void Clear()
        {
            base.Clear();
            OutputCookies = new List<Cookie>();
        }

        public void DoRequest()
        {
            MakeRequest();
            ProcessResponse(Response);
        }

        public abstract void SaveResponseContent(string fileName);
        
        public abstract void ProcessResponse(HttpWebResponse Response);

        public virtual void MakeRequest()
        {
            Response = null;

            if (!UrlHelper.IsUrlCorrect(Url))
                throw new NullReferenceException("Incorrect request URL!");

            string fullUrl = Url;
            string queryString = GetGetString();
            if (queryString.Length > 0)
                fullUrl = Url + (Url.Contains("?") ? "&" : "?") + GetGetString();

            WebRequest webRequest = WebRequest.Create(fullUrl);
            
            if (Context.Proxy != null)
                webRequest.Proxy = Context.Proxy;
            webRequest.ContentType = ContentType;
            webRequest.Method = Method.ToString();

            CookieContainer requestCookieContainer = new CookieContainer();
            
            HttpWebRequest httpRequest = webRequest as HttpWebRequest;
            if (httpRequest == null)
                throw new Exception("Incorrect request!");
            
            httpRequest.CookieContainer = requestCookieContainer;
            Context.InputCookies.Keys.ToList().ForEach(key => httpRequest.CookieContainer.Add(Context.InputCookies[key]));

            SetHeadersToRequest(httpRequest);

            HttpWebResponse response = null;
            CookieCollection cookies = null;
            Stream streamRequest = null;
            try
            {
                if (Method == HttpMethod.POST)
                {
                    string postData = GetPostString();
                    byte[] byteArray = Encoding.UTF8.GetBytes(postData);
                    webRequest.ContentLength = byteArray.Length;

                    streamRequest = webRequest.GetRequestStream();
                    streamRequest.Write(byteArray, 0, byteArray.Length);
                    streamRequest.Close();
                }

                response = (HttpWebResponse)webRequest.GetResponse();
                cookies = requestCookieContainer.GetCookies(webRequest.RequestUri);
            }
            catch (Exception ex)
            {
                LogHelper.Log.Error(ex.Message);
            }
            finally
            {
                if (streamRequest != null)
                    streamRequest.Close();
            }

            Response = response;
            OutputCookies = LoadOutputCookies(cookies);
        }

        protected void SetHeadersToRequest(HttpWebRequest request)
        {
            Context.CommonCustomHeaders.Keys.ToList().ForEach(name => request.Headers.Add(name, Context.CommonCustomHeaders[name]));

            Context.CommonWellknownHeaders.Keys.ToList().ForEach(key =>
                {
                    switch (key)
                    {
                        case HttpRequestHeader.UserAgent :
                            request.UserAgent = Context.CommonWellknownHeaders[HttpRequestHeader.UserAgent];
                            break;
                        case HttpRequestHeader.Connection :
                            request.KeepAlive = Context.CommonWellknownHeaders[HttpRequestHeader.UserAgent].ToLower() == "Keep-Alive".ToLower();
                            break;
                        case HttpRequestHeader.Accept :
                            request.Accept = Context.CommonWellknownHeaders[HttpRequestHeader.Accept];
                            break;
                        case HttpRequestHeader.AcceptLanguage :
                            request.Headers.Add(HttpRequestHeader.AcceptLanguage, Context.CommonWellknownHeaders[HttpRequestHeader.AcceptLanguage]);
                            break;
                        case HttpRequestHeader.AcceptCharset :
                            request.Headers.Add(HttpRequestHeader.AcceptCharset, Context.CommonWellknownHeaders[HttpRequestHeader.AcceptCharset]);
                            break;
                        default :
                            throw new Exception("Unsupported header type: " + key.ToString());
                    }
                });
        }

        protected List<Cookie> LoadOutputCookies(CookieCollection cookies)
        {
            List<Cookie> result = new List<Cookie>();

            if (CollectionHelper.IsNullOrEmpty(cookies))
                return result;

            foreach (Cookie cookie in cookies)
            {
                result.AddIfNotExist(cookie);
            }

            return result;
        }

        protected string GetPostString()
        {
            return GetRequestString(PostMembers);
        }

        protected string GetGetString()
        {
            return GetRequestString(GetMembers);
        }

        protected string GetRequestString(Dictionary<string, string> set)
        {
            string result = String.Empty;

            if (set == null || set.Count == 0)
                return result;

            var values = set
                .Select(kv => String.Format("{0}={1}", kv.Key, HttpUtility.UrlEncode(kv.Value)))
                .ToArray();

            result = String.Join("&", values);

            return result;
        }
    }
}